Before delving into the depths of the language, let's look at an example. Suppose you just finished watching a Godzilla movie, complete with roaring monsters, panic-stricken mobs, fire trucks putting out flames, and so forth, and were inspired to design a game around this theme.
Start by opening up a file, calling it something like g-vs-t.g
,
or some other name appropriate for your type of machine, and then type
this into it:
(game-module "g-vs-t" (title "Godzilla vs Tokyo") (blurb "Godzilla stomps on Tokyo") )
This is a GDL form. It declares the name of the game to be
"g-vs-t"
, gives it a title that prospective players will see in
menus, plus a short description or blurb. The blurb should tell
prospective players what the game is all about, perhaps whether it is
simple or complex, or whether it is one-player or multi-player. Both
title and blurb are examples of properties, which are like slots
in structures.
The game-module
form is optional but recommended; some interfaces
use it to add the game to a list of games that players can choose from.
The general syntax of game-module
form is similar to that used by
nearly all GDL forms; it amounts to a definition of an "object" (such
as a game module or a unit type) with properties (such as name,
description, speed, etc). Some properties are required, and appear at
fixed positions, while others are optional and can be specified in any
order, so they are introduced by name. The general format, then, looks
like
(<object> ... <required properties> ... ... (<property name> <property value>) ... )
There are very few exceptions to this general syntax rule.
(People often have trouble with parentheses in Lisp, but if you follow the same kinds of indentation rules that you always use in C or Pascal, then you will encounter no additional trouble. Also, many editors such as Emacs are intelligent enough to indicate when parentheses match, and automatically do proper indentation.)
Now the first thing you'll need is a monster. In Xconq, each unit has a type, and you define the characteristics attached to the type.
(unit-type monster)
This declares a new unit type named monster
, but says nothing
else about it. Let's use this more interesting form instead:
(unit-type monster (image-name "monster") (help "crushes, crumbles, and chomps") (start-with 1) )
This shows the usual way of describing the monster. In this case,
image-name
is a property that specifies the name of the icon that
will be used to display a monster. (Xconq comes with a library of
over 700 icons, of which "monster"; it looks a little like Godzilla.)
The property start-with
says that each side should start out with
one monster. This isn't quite right, because there should only be one
side with a monster, and this will give each side a monster to start
out with, but we'll see how to fix that later on.
We also need at least one type of terrain for the world:
(terrain-type street (color "gray"))
Streets are to be gray when displayed in color, and get nothing if they are being displayed on a monochrome screen.
These two forms are actually sufficient by themselves to start up a game. (Go ahead and try it.) However, you'll notice that the game is not very interesting. Although each player gets a monster, and an area consisting of all-street terrain is displayed, nobody can actually do anything, since the defaults basically turn off all possible actions.
OK, let's give the monsters the ability to act by putting this form into the file:
(add monster acp-per-turn 4)
The add
form is very useful; it says to modify the existing
type named monster
, setting the property acp-per-turn
to
4, overwriting whatever value might have been there previously. The
acp-per-turn
property gives the monster the ability to act, up to
4 actions in each turn. By default, the ability to act is 1-1 with the
speed of the unit, so the monster can also move into a new cell 4 times
each turn. If you run the game now, you will find that your monster can
now get around just fine.
Why 4? Actually, at this point the exact value doesn't matter, since nothing else is happening. If the speed is 1, then the turns go faster; if the speed is 10, then they go slower and more action happens in a single turn. In a complete design however, the exact speed of each unit can be a critical design parameter, and for this game, I figured that a speed of 4 allowed a monster to cover several cells in a hurry while not being able to get too far. Also, I'm planning to make panic-stricken mobs have a speed of 1, which is the slowest possible. Making actions 1-1 with speed is usually the right thing to do, since then a player will get to move 4 times each turn (later on we will see reasons for other combinations of values).
The add
form works on most types of objects. It has the general
syntax
(add <type(s)/object(s)> <property name> <value(s)>)
The type or object may be a list, in which the value is either given to all members of the list, or if it is a list itself, then the list of values is matched up with the list of types.
To give the monster something to do besides walk around, add buildings as a new unit type:
(unit-type building (image-name "city20")) (table independent-density (building street 500))
The building
type uses an icon that is normally used for a
20th-century city, but it has the right look. The
independent-density
table says how many buildings will be
scattered across in the world. The table
form consists of the
name of the table followed by one or several three-part lists; the two
indexes into the table, and a value. In this case, one index is a unit
type building
, the other is a terrain type street
, and the
value is 500
, which means that we will get about 500 buildings
placed on a 100x100 world (look up the definition of this table in the
index). You need some for testing purposes, otherwise you won't see any
when you start up the game.
We're going to let buildings default to not being able to do anything, since that seems like a reasonable behavior for buildings (although Baba Yaga's hut might be fun...).
By default, buildings act strictly as obstacles; monsters cannot touch them, push them out of the way, or walk over them. In real(?) life of course, monsters hit buildings, so we have to define a sort of combat.
(table hit-chance (monster building 90) (building monster 10) ) (table damage (monster building 1) (building monster 3) ) (add (monster building) hp-max (100 3))
The hit-chance
and damage
tables are the two basic tables
defining combat. The hit chance is simply the percent chance that an
attack will succeed, while the damage is the number of hit points that
will be lost in a successful attack. The unit property hp-max
is
the maximum number of hit points that a unit can have, and by default,
that is also what units normally start with.
Note that the add
form allows lists in addition to single types
and values, in which case it just matches up the two lists. The
add
tries to be smart about this sort of thing; see its official
definition for all the possibilities.
The net effect of these three forms is to say that a monster has a 90% chance of hitting a building and causing 1 hp of damage; three such hits destroy the building. A monster's knuckle might occasionally be skinned doing this; a 10% chance of 3/100 hp damage is not usually dangerous, and feels a little more realistic without complicating things for the player.
Now you can start up a game, and have your monster go over and bash on buildings. Simulated wanton destruction!
By default, a destroyed building vanishes, leaving only empty terrain behind. If you want to leave an obstacle, define a new unit type and let the destroyed building turn into it:
(unit-type rubble-pile) (add building wrecked-type rubble-pile)
In practice, you have to be careful to define the behavior of rubble piles. What happens when a monster hits a rubble pile? Can the rubble pile be cleared away? Does it affect movement? Try these things in a game now and see what happens; sometimes the behavior will be sensible, and sometimes not.
For instance, you will observe that the default behavior is for the rubble pile to be an impenetrable obstacle! The monster can't hit it, and can't stand on it, and in fact can't do anything at all. OK, let's fix it. Monsters are agile enough to climb over all sorts of things, so the right thing is to let the monster co-occupy the cell that the rubble pile is in. The default is to only allow one unit in a cell, but this can be changed:
(table unit-size-in-terrain (rubble-pile t* 0))
This says that while all other units have a size of 1, rubble piles only have a size of 0. By default, each terrain type has a capacity of 1, so this allows one unit and any number of rubble piles to stack together in a cell.
If you try this out, you'll find that the monster can now cross over rubble piles, but still has to bash buildings in order to get them out of the way.
Incidentally, it can cause problems to set a unit size to zero, because it allows infinite stacking. Since buildings and rubble piles don't move, there will never be more than one in a cell, but Xconq will happily let hundreds of units share the same cell, which works, but causes no end of headaches for players confronted with overloaded displays.
Now you've got an "interactive experience" but no game; there's no challenge or goal. You could maybe make a two-or-more-player game where the players race to see who can flatten the mostest the fastest, but that's still not too interesting to anyone past the age of 5. Instead, we need to make some units for the people bravely (or not so bravely) resisting the monster's depredations:
(unit-type mob (name "panic-stricken mob") (image-name "mob")) (unit-type |fire truck| (image-name "firetruck")) (unit-type |national guard| (image-name "soldiers"))
Note that a type's name may have an embedded space, but then you have to put vertical bars around the whole symbol. Things are starting to get complicated, so let's define some shorter synonyms:
(define f |fire truck|) (define g |national guard|) (define humans (mob f g))
You can use the newly defined symbols f
and g
anywhere in
place of the original type names. The symbol humans
is a list of
types, and will be useful in filling several propertys at once.
As with monsters, all these new units should be able to move:
(add humans acp-per-turn (1 6 2))
The speeds here are adjusted so that monsters can chase and run down (and presumably trample to smithereens) mobs and guards, but fire trucks will be able to race away.
Also note the use of a three-element list that matches up with the three
elements in the humans
list. This is a very useful features of
GDL, and used heavily. It can also be a problem, since if you add or
remove elements from the list humans
, every list that it is
supposed to match up with also has to change. Fortunately, Xconq
will tell you if any lists do not match up because they are of different
lengths.
We still need to define some interaction, since monsters and humans can make faces at each other, and get in each other's way, but otherwise cannot interact.
(add table hit-chance (monster humans 50) (humans monster (0 10 70)) )
This time we have to say "add table" because we've already defined the
hit-chance
table and now just want to augment it.
As with the addition of properties, we can use a list in place of a single type.
Last but not least, we need a scorekeeper to say how winning and losing will happen. This is a simple(-minded?) game, so a standard type will be sufficient:
(scorekeeper (do last-side-wins))
The do
property of a scorekeeper may include some rather
elaborate tests, but all we want to is to say that the last side left
standing should be the winner, and the symbol last-side-wins
does
just that.
There might be a bit of a problem with this in practice, since in order to win, the monster has to stomp on all the humans, including fire trucks. But fire trucks can always outrun the monster, and cannot attack it directly either, which leads to a stalemate. You can fix this by zeroing the point value of fire trucks:
(add f point-value 0)
Now, when all the mobs and guards have been stomped, the monster wins automatically, no matter how many fire trucks are left.
As it now stands, your game design requires Xconq to generate all kinds of stuff randomly, such as the initial set of units, terrain, and so forth. However, we are doing a monster movie, so random combinations of monsters and people and terrain don't usually make sense. Instead of trying to define a "reasonable" random setup, we should define a scenario, either by starting a random game, modifying, and saving it, or by text editing. Since online scenario creation is hard to describe in the manual, let's do it with GDL instead.
To define a scenario, we generally need three things: sides, units, and terrain. Now the basic monster movie idea puts one monster up against a bunch of people acting together, so that suggests two sides:
(side 1) (side 2 (name "Tokyo") (adjective "Japanese"))
The 1
and 2
identify the two sides uniquely, since we'll
have to match units up with them in a moment. The side that plays the
monster is really a convenience; players should just be aware of the one
monster unit, so we don't need any sort of names. The other side has
many units, which should be qualified as "Japanese"
, and the side
as a whole really represents the city of Tokyo, so use that for the
side's name.
Now for the units:
(unit monster (s 1) (n "Godzilla")) (unit firetruck (s 2)) (unit firetruck (s 2)) (building 9 10 2) (define b building) ; abbreviate for compactness' sake (b 10 10 2) (b 11 10 2 (n "K-Mart")) (b 12 12 2 (n "Tokyo Hilton")) (b 13 12 2 (n "Hideyoshi's Rice Farm")) (b 14 12 2 (n "Apple Japan")) ;; ... need lots of buildings ...
This example shows two syntaxes for defining units: the first is
introduced by the symbol unit
and requires only a unit type (or
an id, see the definition in xxx), while the second is introduced by the
unit type name itself and requires a position and side. The second form
is more compact and thus suitable for setting up large numbers of units,
while the first form is more flexible, and can be used to modify an
already-created unit. In both cases, the required data may be followed
by optional properties in the usual way.
Also, since the word "building" is a little longwinded, I defined the
symbol "b" to evaluate to "building". GDL has very few predefined
variables, so you can use almost anything, including weird stuff like
"&" and "=". Property names like s
and n
are NOT
predefined variables, so you can use those too if you like.
At this point, you should have a basic game scenario, with one player being Godzilla, and the other trying to keep it from running amuck and flattening all of Tokyo. Have fun!
You can enhance this scenario in all kinds of ways, depending on how ambitious you want to get. Given the basic silliness of the premise, though, it would be more worthwhile to enhance the silliness and speed up the pace, rather than to add features and details. For instance, name the buildings after all the laughingstock places you know of in your own town.
To see where you could go with this, look at the library's
monster
game and its tokyo
scenario, which include fires,
different kinds of terrain, and other goodies.